﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace SimplexEngine
{
    public class StaticMeshPart
    {
        protected virtual String Name { get { return ""; } }

        protected MeshBuffer mesh_buffer = null;

        public virtual IMeshBuffer MeshBuffer { get { return mesh_buffer; } }

        protected internal virtual void SetMeshBuffer(MeshBuffer buffer)
        {
            mesh_buffer = buffer;
        }

        public virtual Material Material { get { return material; } set { material = value; } }
        protected Material material = null;
    }

    public class StaticMesh
    {
        static protected NameGenerator name_generator = new NameGenerator("MeshPart");
        protected Dictionary<String, StaticMeshPart> mesh_parts = new Dictionary<String, StaticMeshPart>();

        public Matrix[] Bones { get { return bones; } set { bones = value; } }
        protected Matrix[] bones ;

        public Dictionary<String, StaticMeshPart> MeshParts { get { return mesh_parts; } }

        public void AddMeshPart(StaticMeshPart mesh_part)
        {
            AddMeshPart(name_generator.Generate(), mesh_part);
        }

        public void AddMeshPart(String name, StaticMeshPart mesh_part)
        {
            mesh_parts.Add(name, mesh_part);
        }

        public static StaticMeshInstance CreateStaticMeshInstance(Resource static_mesh)
        { 
            return new StaticMeshInstance(static_mesh);
        }
    }

    public class StaticMeshInstance
    {
        public StaticMeshInstance(Resource source)
        {
            mesh_resource = source;
        }

        public virtual List<RenderElement> CreateRenderElements(SceneObject scene_object)
        {
            List<RenderElement> result = new List<RenderElement>();

            StaticMesh mesh = mesh_resource.GetObject<StaticMesh>();

            if (mesh != null)
            {               
                if (materials == null)
                    BuildMaterials(mesh);

                foreach (KeyValuePair<String, Material> material in materials)
                {
                    if (material.Value.Effect != null)
                        result.Add(new RenderElement(mesh.MeshParts[material.Key].MeshBuffer, material.Value, scene_object));
                }
            }

            return result;
        }

        protected virtual void BuildMaterials(StaticMesh mesh)
        {
            materials = new Dictionary<String, Material>();
            foreach (KeyValuePair<String, StaticMeshPart> mesh_part in mesh.MeshParts)
                materials.Add(mesh_part.Key, mesh_part.Value.Material.Clone());

            if (material_to_set != null)
            {
                SetMaterialToAllPart(material_to_set, clone_material, copy_old_material_parameters);
                material_to_set = null;
            }
        }

        public virtual void SetMaterialToAllPart(Material material, Boolean clone, Boolean copy_old_parameters)
        {
            if (materials != null)
            {
                String[] keys = materials.Keys.ToArray();
                foreach (String key in keys)
                {
                    Material new_material = clone ? material.Clone() : material;
                    if (copy_old_parameters)
                        new_material.SetParameters(materials[key].GetParameters());
                    materials[key] = new_material;
                }
            }
            else
            {
                material_to_set = material;
                clone_material = clone;
                copy_old_material_parameters = copy_old_parameters;
            }
        }
        protected Material material_to_set = null;
        protected Boolean clone_material = true;
        protected Boolean copy_old_material_parameters = false;

        protected Dictionary<String, Material> materials = null;
        protected Resource mesh_resource;

        internal Resource MeshResource { get { return mesh_resource; } }
    }
}
